{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"../common/rfsoc_book_banner.jpg\" alt=\"University of Strathclyde\" align=\"left\">"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block\" style=\"background-color: #c7b8d6; padding: 10px\">\n",
    "    <p style=\"color: #222222\">\n",
    "        <b>Note:</b>\n",
    "        <br>\n",
    "        This Jupyter notebook uses hardware features of the Zynq UltraScale+ RFSoC device. Therefore, the notebook cells will only execute successfully on an RFSoC platform.\n",
    "    </p>\n",
    "</div>\n",
    "\n",
    "# Notebook Set G\n",
    "\n",
    "---\n",
    "\n",
    "## 01 - RFSoC Radio System\n",
    "This demonstrator presents a Binary Phase Shift Keying (BPSK) and Quadrature Phase Shift Keying (QPSK) radio system design for the Zynq UltraScale+ RFSoC [1]. The BPSK/QPSK radio implements a full transmitter and receiver design including frame based transmission of BPSK and QPSK modulated data, receiver synchronisation, frame synchronisation, and payload extraction. This demonstration employs the RFSoC RF Data Converters (RF DCs) to support the transmission and reception of BPSK and QPSK waveforms. This system is based on the work described in [2].\n",
    "\n",
    "## Table of Contents\n",
    "* [1. Introduction](#introduction)\n",
    "    * [1.1. Hardware Setup](#hardware-setup)\n",
    "    * [1.2. Software Setup](#software-setup)\n",
    "* [2. RFSoC Python Libraries](#rfsoc-python-libraries)\n",
    "    * [2.1. The xrfclk Module](#the-xrfclk-module)\n",
    "    * [2.2. The xrfdc Module](#the-xrfdc-module)\n",
    "* [3. The RFSoC Radio System](#the-rfsoc-radio-system)\n",
    "    * [3.1. Controlling the System](#controlling-the-system)\n",
    "* [4. Frame Generation](#frame-generation)\n",
    "* [5. Conclusion](#conclusion)\n",
    "\n",
    "## References\n",
    "* [1] - [Xilinx, Inc, \"USP RF Data Converter: LogiCORE IP Product Guide\", PG269, v2.4, November 2020](https://www.xilinx.com/support/documentation/ip_documentation/usp_rf_data_converter/v2_4/pg269-rf-data-converter.pdf)\n",
    "* [2] - [Stewart, R. W., Barlee, K. W., Atkinson, D. S. W., & Crockett, L. H. (2015). Software Defined Radio using MATLAB & Simulink and the RTL-SDR. (1 ed.)](https://www.desktopsdr.com/)\n",
    "* [3] - [Xilinx, Inc, \"PYNQ SD Build Packages on GitHub: xrfdc Python Module](https://github.com/Xilinx/PYNQ/tree/image_v2.7/sdbuild/packages/xrfdc)\n",
    "* [4] - [Xilinx, Inc, \"PYNQ SD Build Packages on GitHub: xrfclk Python Module](https://github.com/Xilinx/PYNQ/tree/image_v2.7/sdbuild/packages/xrfclk)\n",
    "* [5] - [Xilinx, Inc, \"EmbeddedSW Repository on GitHub: xrfdc](https://github.com/Xilinx/embeddedsw/tree/release-2020.2/XilinxProcessorIPLib/drivers/rfdc)\n",
    "\n",
    "## Revision\n",
    "* **v1.0** | 19/05/23 | *First Revision*\n",
    "\n",
    "---\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Introduction <a class=\"anchor\" id=\"introduction\"></a>\n",
    "This notebook describes the hardware setup necessary to operate the radio design. We will also explore related RFSoC Python libraries, which include the xrfdc and xrfclk modules. An overview of the BPSK and QPSK transceiver design for the RFSoC will be presented. Lastly, the frame generation for transferring data between the RF DAC and RF ADC will be discussed.\n",
    "\n",
    "Let us begin by setting up the hardware as given below."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### 1.1. Hardware Setup <a class=\"anchor\" id=\"zcu208_and_zcu216\"></a>\n",
    "The ZCU216 development boards require the SMA and clock connections to be setup correctly. We will begin with connecting the clocks from the Clk104 card to the development board. Then connect the ADC and DAC to the HW_XM_655 card's RF baluns.\n",
    "\n",
    "Before you begin with the setup below, ensure that the Clk104 card and HW_XM_655 card is securely connected to the ZCU216 development board.\n",
    "\n",
    "#### 1.1.1. CLK104 Setup <a class=\"anchor\" id=\"clk104_setup\"></a>\n",
    "On the Clk104 board, make the following connections as shown in [Figure 1](#fig-1).\n",
    "\n",
    "<a class=\"anchor\" id=\"fig-1\"></a>\n",
    "<figure>\n",
    "<img src='images/clk104_setup.jpg' height='50%' width='50%'/>\n",
    "    <figcaption><b>Figure 1: Setup of the clk104 add-on board on the ZCU216.</b></figcaption>\n",
    "</figure>\n",
    "\n",
    "Ensure that the clocks above are connected as follows:\n",
    "* DAC_229_CLK to DAC_RFCLK_B (P & N).\n",
    "* ADC_225_CLK to ADC_RFCLK_B (P & N).\n",
    "\n",
    "If you do not make the correct connection, the demonstration will not operate correctly.\n",
    "\n",
    "#### 1.1.2. ADC Setup <a class=\"anchor\" id=\"adc_setup\"></a>\n",
    "The next step is to connect the ADC to the RF Balun on the HW_XM_655 card. See the image below in [Figure 2](#fig-2) on how to make this connection successfully. You will need the rectangle connector that contains a strip of wires with SMAs attached to make this connection. This connector is known as the Carlisle SMA 8 Cable Assemblies in the kit contents page supplied with your development board.\n",
    "\n",
    "<a class=\"anchor\" id=\"fig-2\"></a>\n",
    "<figure>\n",
    "<img src='images/zcu2xx_adc_setup.jpg' height='50%' width='50%'/>\n",
    "    <figcaption><b>Figure 2: Setting-up the ADC on the HW_XM_655 card.</b></figcaption>\n",
    "</figure>\n",
    "\n",
    "Ensure that the ADC connections are setup as follows:\n",
    "* JHC5 P0_255 to J18\n",
    "* JHC5 N01_255 to J16\n",
    "\n",
    "This step is a little difficult as there are many small wires. Please take your time setting up the ADC to prevent errors.\n",
    "\n",
    "#### 1.1.3. DAC Setup <a class=\"anchor\" id=\"dac_setup\"></a>\n",
    "The DAC is connected to the RF Balun on the HW_XM_655 card using the connections shown in [Figure 3](#fig-3) below.\n",
    "\n",
    "<a class=\"anchor\" id=\"fig-3\"></a>\n",
    "<figure>\n",
    "<img src='images/zcu2xx_dac_setup.jpg' height='50%' width='50%'/>\n",
    "    <figcaption><b>Figure 3: Setting-up the DAC on the HW_XM_655 card.</b></figcaption>\n",
    "</figure>\n",
    "\n",
    "Ensure that the DAC connections are setup as follows:\n",
    "* JHC3 P0_230 to J39\n",
    "* JHC3 N0_230 to J37\n",
    "\n",
    "Take your time with this step to prevent errors during the demonstration.\n",
    "\n",
    "#### 1.1.4. Loopback Setup <a class=\"anchor\" id=\"loopback_setup\"></a>\n",
    "We can now connect each RF Balun together using an SMA cable contained in your ZCU216 box. The image below in [Figure 4](#fig-4) shows how to make this connection.\n",
    "\n",
    "<a class=\"anchor\" id=\"fig-4\"></a>\n",
    "<figure>\n",
    "<img src='images/zcu2xx_sma_conn.jpg' height='50%' width='50%'/>\n",
    "    <figcaption><b>Figure 4: Connecting the ADC to the DAC in loopback mode on the HW_XM_655 card.</b></figcaption>\n",
    "</figure>\n",
    " \n",
    "The connection shown in the image above is the following:\n",
    "* J17 (LFB_ADC_02) to J38 (LFB_DAC_01)\n",
    "\n",
    "Your ZCU216 development board is now ready for the demonstration."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-box alert-danger\">\n",
    "<b>Caution:</b>\n",
    "    In this demonstration, we generate signals using the RFSoC development board. Your device should be setup in loopback mode. You should understand that the RFSoC platform can also transmit RF signals wirelessly. Remember that unlicensed wireless transmission of RF signals may be illegal in your geographical location. Radio signals may also interfere with nearby devices, such as pacemakers and emergency radio equipment. Note that it is also illegal to intercept and decode particular RF signals. If you are unsure, please seek professional support.\n",
    "</div>\n",
    "\n",
    "### 1.2. Software Setup <a class=\"anchor\" id=\"software-setup\"></a>\n",
    "Begin by programming the FPGA bitstream and initialising the PYNQ overlay design. To do this, we need to import the `rfsoc_radio` package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from rfsoc_radio.overlay import RadioOverlay"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can simply initialise the overlay by downloading the bitstream and executing the drivers. Upon running the cell below, a synchronisation test will be performed to ensure your system is ready for the demonstration. If these tests fail, reset the notebook and double-check the loopback connection. Then, simply run the notebook again."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ol = RadioOverlay(run_test=True, debug_test=False)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. RFSoC Python Libraries <a class=\"anchor\" id=\"rfsoc-python-libraries\"></a>\n",
    "There are two RFSoC Python libraries used in this radio design. These are the xrfclk and xrfdc modules. The xrfclk module is responsible for configuring the RFSoC's LMK and LMX clock devices. The xrfdc module contains a set of bindings for controlling the RFSoC's Data Converters (RF DCs). Each are discussed further below. \n",
    "\n",
    "### 2.1. The xrfclk Module <a class=\"anchor\" id=\"the-xrfclk-module\"></a>\n",
    "The xrfclk module can be imported as shown in the following code cell. There is one particular method that will be useful when developing your own RFSoC designs. This method is:\n",
    "```python\n",
    "xrfclk.set_ref_clks(lmk_freq, lmx_freq)\n",
    "```\n",
    "We can query the docstring for this method by running the code cell below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import xrfclk\n",
    "\n",
    "xrfclk.set_ref_clks?"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we can see, this method accepts a set of frequencies for an LMK and LMX device. These devices are used to derive the RF DC sampling clocks for your RFSoC platform. Each RFSoC platform contains a different set of LMK and LMX devices. Review the table below to determine the LMK device that your RFSoC platform contains.\n",
    "\n",
    "<a class=\"anchor\" id=\"tab-1\"></a>\n",
    "<figure>\n",
    "    <figcaption><b>Table 1: LMK and LMX devices on each supported RFSoC platform.</b></figcaption>\n",
    "    <br>\n",
    "    <table style=\"width:50%\">\n",
    "        <th></th>\n",
    "        <th>ZCU111</th>\n",
    "        <th>RFSoC2x2</th>\n",
    "        <th>ZCU208</th>\n",
    "        <th>ZCU216</th>\n",
    "        <th>RFSoC4x2</th>\n",
    "      <tr style=\"text-align:center\">\n",
    "        <td><b>LMX Device<b></td>\n",
    "        <td>LMX2594</td>\n",
    "        <td>LMX2594</td>\n",
    "        <td>LMX2594</td>\n",
    "        <td>LMX2594</td>\n",
    "        <td>LMX2594</td>\n",
    "      </tr>\n",
    "      <tr style=\"text-align:center\">\n",
    "        <td><b>LMK Device<b></td>\n",
    "        <td>LMK04208 (default frequency is 122.88)</td>\n",
    "        <td>LMK04832 (default frequency is 122.88)</td>\n",
    "        <td>LMK04828 (default frequency is 245.76)</td>\n",
    "        <td>LMK04828 (default frequency is 245.76)</td>\n",
    "        <td>LMK04828 (default frequency is 245.76)</td>\n",
    "      </tr>\n",
    "    </table>\n",
    "</figure>\n",
    "        \n",
    "We cannot use any arbitrary frequency to program the LMK and LMX devices. There are a set of clock files provided for configuring these devices to a particular frequency. These files are found in the xrfclk module. We can print the available files using the code cell below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "files = sorted(os.listdir(os.path.dirname(xrfclk.__file__)))\n",
    "for file in files:\n",
    "    if 'LMK' in file or 'LMX' in file:\n",
    "        print(file)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You should be able to match the LMK device on your RFSoC platform with a clock file given above. Further information about the xrfclk module can be found in [4]."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2. The xrfdc Module <a class=\"anchor\" id=\"the-xrfdc-module\"></a>\n",
    "The purpose of the xrfdc module is to reduce the complexity of configuring the RF DCs. The xrfdc module contains a set of Python bindings for the C class driver given in [5]. We can begin by importing the xrfdc package as shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import xrfdc"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the xrfdc library is imported before initialising the PYNQ overlay class, then it will be automatically assigned to the overlay's RF DC IP core object. We can obtain a handle for the RF DCs as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Obtain handle for the RF Data Converters\n",
    "rfdc = ol.usp_rf_data_converter"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The RF DC has ADC and DAC tiles, which also contain individual ADC and DAC blocks. We can obtain a handle to the second ADC tile as shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Obtain handle for ADC Tile 2\n",
    "tile = rfdc.adc_tiles[1]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can set the reference LMX frequency of this tile and its corresponding sampling frequency using the tile's `DynamicPLLConfig` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set the PLL configuration\n",
    "tile.DynamicPLLConfig(1, 409.6, 1024)\n",
    "\n",
    "# Read back the PLL configuration\n",
    "tile.PLLConfig"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, the tile has now been configured to use a sampling frequency of 1.024GSa/s.\n",
    "\n",
    "We can also obtain a handle to an individual ADC block. For instance, the code cell below obtains the first ADC block of the second tile."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Obtain handle for ADC Tile 2 Block 0\n",
    "block = tile.blocks[0]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In a similar way to the tile, we can configure the block using its associated methods and properties. For example, we can configure the mixer settings (which controls the centre frequency of the fine mixer)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set the mixer settings\n",
    "block.MixerSettings = {\n",
    "    'CoarseMixFreq'  : xrfdc.COARSE_MIX_BYPASS,\n",
    "    'EventSource'    : xrfdc.EVNT_SRC_TILE,\n",
    "    'FineMixerScale' : xrfdc.MIXER_SCALE_1P0,\n",
    "    'Freq'           : -101,\n",
    "    'MixerMode'      : xrfdc.MIXER_MODE_R2C,\n",
    "    'MixerType'      : xrfdc.MIXER_TYPE_FINE,\n",
    "    'PhaseOffset'    : 0.0\n",
    "}\n",
    "\n",
    "# Print back mixer settings\n",
    "block.MixerSettings"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Reading back the mixer settings, we can see that the centre frequency has now been set to 101MHz. There are many more methods and properties for you to explore. See [1] and [3] for further information. \n",
    "\n",
    "Finally, it is also important to note that we are unable to configure all features of the RF DC from software. Some capabilities can only be configured when designing the FPGA system."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. The RFSoC Radio System <a class=\"anchor\" id=\"the-rfsoc-radio-system\"></a>\n",
    "An overview of the RFSoC radio system is given in [Figure 5](fig-5). Two hardware accelerators are provided; one to transmit data, and the other to receive data. Each accelerator is independant of one another and do not communicate. The transmitter modulates 100 kSa/s of data and it interpolates the signal to 1.024 GSa/s with subsequent interpolation stages. The RF DAC then transmits the data. The receiver is connected to the transmitter using an SMA loopback cable. The RF ADC will initially decimate the data and the receiver hardware accelerator will be responsible for synchronising to the signal and extracting the modulated data.\n",
    "\n",
    "<figure> <a class=\"anchor\" id=\"fig-5\"></a>\n",
    "    <img src=\"images/system_overview.png\" style=\"width: 70%;\"/>\n",
    "    <figcaption><b>Figure 5: Overview of the radio demonstration system on RFSoC and PYNQ.</b></figcaption>\n",
    "</figure>\n",
    "\n",
    "A DMA controller is provided to transfer data from Jupyter Labs to the transmitter. Another DMA is provided to transfer data from the receiver to Jupyter Labs. Jupyter will be used to control the hardware accelerators and inspect the transmitter's data generation stages and receiver's synchronisation stages.\n",
    "\n",
    "### 3.1. Controlling the System <a class=\"anchor\" id=\"controlling-the-system\"></a>\n",
    "This demonstration provides a useful tool for interacting with the radio system. A radio dashboard, made entirely using the `ipywidgets` library, will allow you to change the mixer frequencies of the RF DAC and RF ADC. You can also choose to switch-off parts of the radio system such as the transmitter, and various parts of the receiver. You can load the radio dashboard by running the code cell below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ol.dashboard()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the next notebook, we will use the radio dashboard to interact with the radio system while it is running.\n",
    "\n",
    "The radio dashboard also reports the frequency offset that is corrected in the coarse frequency synchronisation stage. Be aware that the coarse frequency synchroniser can only correct 1.6MHz offsets. If the signal's offset frequency is larger than 1.6MHz, the reported frequency offset will be incorrect."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Frame Generation <a class=\"anchor\" id=\"frame-generation\"></a>\n",
    "Before we explore the transmit and receive pipelines in the next notebook, the frame used to transfer data should be described. Data frames are often required in communication systems as they allow the receiver to synchronise to the start of the payload (the data we would like to extract). Usually the transmitter sends a known sequence of bits, and these bits are detected in the receiver through correlation. In this system, the extended barker sequence [2] is used as the known sequence of bits for frame synchronisation.\n",
    "\n",
    "In addition to the barker sequence, the transmitter must place other important information in a data frame before transmission. Some of these are listed below:\n",
    "\n",
    "* Random data that allows the synchroniser to be exercised.\n",
    "* The extended barker sequence or known sequence of bits.\n",
    "* The frame number (if there are a series of frames transmitted sequentially).\n",
    "* The start flag indicating the first frame in the sequence.\n",
    "* The end flag indicating the last frame in the sequence.\n",
    "* The data frame length.\n",
    "* The payload.\n",
    "* Zero padding.\n",
    "* Anything else that is useful for your application.\n",
    "\n",
    "The radio demonstration system uses a custom data frame. To keep things simple, the transmitter software wrapper only sends data frames that are 64 bytes long. This means that the frame is aligned to 4 bytes in memory, preventing unaligned access. The data frame format used by the demonstrator is shown in [Figure 6](fig-6).\n",
    "\n",
    "<figure>\n",
    "<img src=\"images/data_frame.png\" style=\"width: 80%;\"/>\n",
    "    <figcaption><b>Figure 6: Data frame structure for transmit and receive packets.</b></figcaption>\n",
    "</figure>\n",
    "\n",
    "As shown, the data frame is exactly 64 bytes long. If the data frame uses less than 44 bytes of data for the payload, it is zero padded as required. Each of the data frame entries shown are used in this demonstration system."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Conclusion <a class=\"anchor\" id=\"conclusion\"></a>\n",
    "In this notebook, we reviewed the hardware and software setup for the RFSoC radio system. The xrfclk and xrfdc modules were also introduced. An overview of the RFSoC radio system was given and the data frame used by the transmitter and receiver was explored.\n",
    "\n",
    "In the next notebook, we will explore the transmitter and receiver pipelines and perform live introspection of the radio system."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "[⬅️ Previous Notebook](../notebook_F/02_rfsoc_frequency_planner.ipynb) || [Next Notebook 🚀](02_rfsoc_radio_observe.ipynb)\n",
    "\n",
    "Copyright © 2023 Strathclyde Academic Media\n",
    "\n",
    "---\n",
    "---"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
